#
# Copyright (c) 2009, 2010, Davide Cittaro
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the <organization> nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# This module includes most of the helper function
# which I coulnd't put into classes...

import xml.dom.minidom
import sequence


def initializeMap(theMap):
  graphML = theMap.createElement("graphml")
  graphML.setAttribute("xmlns", "http://graphml.graphdrawing.org/xmlns")
  graphML.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")
  graphML.setAttribute("xmlns:y","http://www.yworks.com/xml/graphml")
  graphML.setAttribute("xsi:schemaLocation", "http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd")
  theMap.appendChild(graphML)
#  <key for="graphml" id="d0" yfiles.type="resources"/>
  k1 = theMap.createElement("key")
  for a, v in [("for","graphml"), ("id","d0"), ("yfiles.type","resources")]:
    k1.setAttribute(a, v)
  graphML.appendChild(k1)
#  <key attr.name="url" attr.type="string" for="node" id="d1"/>
  k2 = theMap.createElement("key")
  for a, v in [("attr.name","url"), ("attr.type","string"), ("for","node"), ("id", "d1")]:
    k2.setAttribute(a, v)
  graphML.appendChild(k2)
#  <key attr.name="description" attr.type="string" for="node" id="d2"/>
  k3 = theMap.createElement("key")
  for a, v in [("attr.name","description"), ("attr.type","string"), ("for","node"), ("id", "d2")]:
    k3.setAttribute(a, v)
  graphML.appendChild(k3)
#  <key for="node" id="d3" yfiles.type="nodegraphics"/>
  k4 = theMap.createElement("key")
  for a, v in [("for","node"), ("id","d3"), ("yfiles.type","nodegraphics")]:
    k4.setAttribute(a, v)
  graphML.appendChild(k4)
#  <key attr.name="Description" attr.type="string" for="graph" id="d4">
#    <default/>
#  </key>
  k5 = theMap.createElement("key")
  for a, v in [("attr.name","Description"), ("attr.type","string"), ("for","graph"), ("id", "d4")]:
    k5.setAttribute(a, v)
  graphML.appendChild(k5)
  k6 = theMap.createElement("default")
  k5.appendChild(k6)
#  <key attr.name="url" attr.type="string" for="edge" id="d5"/>
  k7 = theMap.createElement("key")
  for a, v in [("attr.name","url"), ("attr.type","string"), ("for","edge"), ("id", "d5")]:
    k7.setAttribute(a, v)
  graphML.appendChild(k7)
#  <key attr.name="description" attr.type="string" for="edge" id="d6"/>
  k8 = theMap.createElement("key")
  for a, v in [("attr.name","description"), ("attr.type","string"), ("for","edge"), ("id", "d5")]:
    k8.setAttribute(a, v)
  graphML.appendChild(k8) 
#  <key for="edge" id="d7" yfiles.type="edgegraphics"/>
  k9 = theMap.createElement("key")
  for a, v in [("for","edge"), ("id","d7"), ("yfiles.type","edgegraphics")]:
    k9.setAttribute(a, v)
  graphML.appendChild(k9)

  # create the <graph> node
  graphNode = theMap.createElement("graph")
  graphNode.setAttribute("edgedefault", "directed")
  graphNode.setAttribute("id", "G")
  graphML.appendChild(graphNode)
  

  # add final <data> node (useless?)
  dataNode = theMap.createElement("data")
  dataNode.setAttribute("key", "d0")
  graphML.appendChild(dataNode)
  r1 = theMap.createElement("y:Resources")
  dataNode.appendChild(r1)


def createProteinNode(theMap, id, x, y, proteinName):
  # get the <graph> node first
  graphNode = theMap.getElementsByTagName("graph")[0]
  
  thisNode = theMap.createElement("node")
  thisNode.setAttribute("id", id)
  graphNode.appendChild(thisNode)  
  #add data
  nodeData = theMap.createElement("data")
  nodeData.setAttribute("key", "d3")
  thisNode.appendChild(nodeData)
  nodePar = theMap.createElement("y:ShapeNode")
  nodeData.appendChild(nodePar)
  yPar1 = theMap.createElement("y:Geometry")
  yPar1.setAttribute("height", "55.0")
  yPar1.setAttribute("width", "55.0")
  yPar1.setAttribute("x", str(x))
  yPar1.setAttribute("y", str(y))
  nodePar.appendChild(yPar1)
  yPar2 = theMap.createElement("y:Fill")
  yPar2.setAttribute("hasColor", "false")
  yPar2.setAttribute("transparent", "false")
  nodePar.appendChild(yPar2)
  yPar3 = theMap.createElement("y:BorderStyle")
  yPar3.setAttribute("color", "#000000")
  yPar3.setAttribute("type", "line")
  yPar3.setAttribute("width", "1.0")
  nodePar.appendChild(yPar3)
  yPar4 = theMap.createElement("y:NodeLabel")
  yPar4.setAttribute("alignment", "center")
  yPar4.setAttribute("autoSizePolicy", "content")
  nodePar.appendChild(yPar4)
  label = theMap.createTextNode(str(proteinName))
  yPar4.appendChild(label)
  yPar5 = theMap.createElement("y:Shape")
  yPar5.setAttribute("type", "ellipse")
  nodePar.appendChild(yPar5)

def createInteractionNode(theMap, id, x, y, interactionProperties):
  graphNode = theMap.getElementsByTagName("graph")[0]

  thisNode = theMap.createElement("node")
  thisNode.setAttribute("id", id)
  graphNode.appendChild(thisNode)  
  #add data
  nodeData = theMap.createElement("data")
  nodeData.setAttribute("key", "d3")
  thisNode.appendChild(nodeData)
  nodePar = theMap.createElement("y:ShapeNode")
  nodeData.appendChild(nodePar)
  yPar1 = theMap.createElement("y:Geometry")
  yPar1.setAttribute("height", "60.0")
  yPar1.setAttribute("width", "120.0")
  yPar1.setAttribute("x", str(x))
  yPar1.setAttribute("y", str(y))
  nodePar.appendChild(yPar1)
  yPar2 = theMap.createElement("y:Fill")
  yPar2.setAttribute("hasColor", "false")
  yPar2.setAttribute("transparent", "false")
  nodePar.appendChild(yPar2)
  yPar3 = theMap.createElement("y:BorderStyle")
  yPar3.setAttribute("color", "#000000")
  yPar3.setAttribute("type", "line")
  yPar3.setAttribute("width", "1.0")
  nodePar.appendChild(yPar3)
  yPar4 = theMap.createElement("y:NodeLabel")
  yPar4.setAttribute("alignment", "center")
  yPar4.setAttribute("autoSizePolicy", "node_size")
  yPar4.setAttribute("configuration", "CroppingLabel")
  nodePar.appendChild(yPar4)
  label = theMap.createTextNode(str(interactionProperties))
  yPar4.appendChild(label)
  yPar5 = theMap.createElement("y:Shape")
  yPar5.setAttribute("type", "rectangle")
  nodePar.appendChild(yPar5)

def createEdge(theMap, id, source, target, aaPosition):
  graphNode = theMap.getElementsByTagName("graph")[0]

  thisEdge = theMap.createElement("edge")
  thisEdge.setAttribute("id", id)
  thisEdge.setAttribute("source", source)
  thisEdge.setAttribute("target", target)
  graphNode.appendChild(thisEdge)
  edgeData = theMap.createElement("data")
  edgeData.setAttribute("key", "d7")
  thisEdge.appendChild(edgeData)
  edgePar = theMap.createElement("y:PolyLineEdge")
  edgeData.appendChild(edgePar)
  yPar1 = theMap.createElement("y:LineStyle")
  for a, v in [("color","#000000"), ("type","line"), ("width","1.0")]:
    yPar1.setAttribute(a, v)
  edgePar.appendChild(yPar1)
  yPar2 = theMap.createElement("y:Arrows")
  for a, v in [("source","none"), ("targe","none")]:
    yPar2.setAttribute(a, v)
  edgePar.appendChild(yPar2)
  yPar3 = theMap.createElement("y:EdgeLabel")
  for a, v in [("alignment","center"), ("lineColor","#000000"), ("distance","2.0"), ("fontFamily","Dialog"), ("height","18"), ("modelName","side_slider"), ("ratio","0.3333333333333333"), ("width","27")]:
    yPar3.setAttribute(a, v)
  edgePar.appendChild(yPar3)
  label = theMap.createTextNode(str(aaPosition))
  yPar3.appendChild(label)
  yPar4 = theMap.createElement("y:BendStyle")
  yPar4.setAttribute("smoothed", "false")
  edgePar.appendChild(yPar4)
  
  
  

def createMap(InteractionList):
  # initialize the map
  nNumber = 0
  eNumber = 0
  prot2node = {}
  node2prot = {}
  proteinNames = set()
  theMap = xml.dom.minidom.Document()
  
  # add the header
  initializeMap(theMap)
  
  # add the interactions
  # first of all we have to create a map of proteinName:nodeName
  # to avoid duplicates
#  outputString = prot1.accession() + ':' + str(interaction.xPositions()[0]) + ' ' + prot2.accession() + ':' + str(interaction.xPositions()[1])

  for interaction in InteractionList:
    (prot1, prot2) = interaction.linkedProteins()
    proteinNames.add(prot1.accession())
    proteinNames.add(prot2.accession())
  for protein in proteinNames:
    id = "n" + str(nNumber)
    x = 100 * nNumber + 10
    y = 100 
    prot2node[protein] = id
    node2prot[id] = protein
    nNumber += 1
    # create the protein node
    createProteinNode(theMap, id, x, y, protein)
  
  # now create the interaction nodes
  # and the edges?
  nNumber = 0
  for interaction in InteractionList:
    iid = "i" + str(nNumber)
    
    x = 130 * nNumber + 10
    y = 400 

    iProperties = "MMS: " + "%.4f" % interaction.aggregateMatchScore() + "\nG: " + "%.4f" % interaction.GScore()
    createInteractionNode(theMap, iid, x, y, iProperties)
    nNumber += 1
    
    # get again protein names
    (prot1, prot2) = interaction.linkedProteins()
    acc1 = prot1.accession()
    acc2 = prot2.accession()
    
    # get xlink positions. These will be the label for the edges
    (aa1, aa2) = interaction.xPositions()
    eid = "e" + str(eNumber)
    
    # create the edges
    createEdge(theMap, eid, prot2node[acc1], iid, aa1)
    eNumber += 1
    createEdge(theMap, eid, prot2node[acc2], iid, aa2)
    eNumber += 1
    
  
  
  # return the text to be printed
  return theMap.toprettyxml(indent="", newl="")
  
def saveMap(fname, InteractionList):
  fh = open(fname, 'w')
  fh.write(createMap(InteractionList))
  fh.close()
